/*
* ============================================================================
*  Name        : sea.h
*  Part of     : Finite state machine framework
*  Description : State machine class definition
*  Version     : %version: %
*  
*
* ============================================================================
*/
/**
 * @file sea.h
 * @brief
 * @ref sea::StateMachine
 *
 * @ingroup StateMachineFramework
 */

#if !defined( __sea__sm_h__ )
#define __sea__sm_h__

#include "sea_platform.h"
#include "sea_trace.h"
#include "sea_meta_system.h"
#include "sea_timer_server.h"
#include "sea_timer.h"
#include "sea_engine.h"
#include "sea_memtracker.h"
#include "sea_iterator.h"

namespace sea
{
    class Manager;
    class Event;
    class Queue;
    class Parser;
    class SystemStateMachine;
    class MetaAction;
    class MetaEvent;

    /**
     * @class BufferOf
     * @brief
     *  Generic buffer of simple types. \b INTERNAL \b API. 
     *
     * @ingroup StateMachineFramework
     */
    template< typename T >
    class BufferOf : public Object
    {
    public:
        BufferOf( T* aCode ) : Object( reinterpret_cast< char* >( aCode ) ), m_code( aCode ) { ASSERT_DEBUG( aCode ); }
        ~BufferOf( ){ delete m_code; }
    private:
        T* m_code;
    };

    typedef BufferOf< int > ExecCode;

    /**
     * @struct MetaSystemRegistryEntry
     * @brief State machine registry entry accomodating \n
     *  \b a) State machine parsing stage \n
     *  \b b) Runtime execution \n
     * Need to be able to find MetaAction by action function name and index. Same for the events.\n
     * Also this record contains logic variants or compiled state machines. \b INTERNAL \b API. 
     */
    class MetaSystemRegistryEntry : public Object
    {
    public:
        MetaSystemRegistryEntry( String name ) : m_name( name ) { }
        ~MetaSystemRegistryEntry( );

        String m_name;                              ///< State machine class name
        vector< MetaAction > m_actions;             ///< Action function( AF ) array
        map< string, MetaAction > m_action_map;     ///< Mapping AF name -> MetaAction -> index in the m_actions
                                                    ///< while parsing JSON/XML state machine
        vector< MetaEvent > m_events;               ///< Events array
        map< String, MetaEvent > m_event_map;       ///< To map Event name -> MetaEvent -> index in the m_events
                                                    ///< while parsing JSON/XML state machine
        map< int, sea::aref > m_variant_map;        ///< It is possible to have many behaviours for one state machine class

#if defined( SEA_LOG ) || defined( STORE_BIN_CODE )
        map< int, String > m_offset2name;           ///< Map state's offset to name
        map< int, int > m_offset2index;             ///< Map state's offset to index
#endif
        OsMutex m_lock;
    };

    /** 
     * @class StateMachine sea.h "sea.h"
     * @brief 
     *   State Machine Object. Holds all information about state machine: body, events, actions, current  <br>
     *  state of the life cycle. <br>
     *
     * @ingroup StateMachineFramework
     */
    class StateMachine : public OsSemaphore
    {
        /**
         * Enable full access for internal classes
         */
        friend class Engine;
        friend class Manager;
        friend class SystemStateMachine;
        friend class Parser;
        friend class Event;

        friend class MetaSystem;
        friend class MetaAction;
        friend class MetaEvent;
    public:
        /**
         * Action function type
         */
        typedef void ( StateMachine::* ActionFunction )( void* );

    public:

        /**
         * Types of the source code
         */
        enum Type
        {
            eFile,                          ///< Source in a file
            eString,                        ///< String format
            eBinary                         ///< Pre-compiled, binary format. See sm_parser.cpp
        };

        /**
         * State machine execution mode
         */
        enum ExecutionMode
        {
            RunDefault,                     ///< Use run mode specified in state machine body
            RunSync,                        ///< run all synchronously now
            RunParser,                      ///< same as RunSync, but no waiting for external event
                                            ///< basic event loop
            RunAsync,                       ///< run each in a separate thread 
            RunAsyncBatch,                  ///< run all in one new thread 
            RunWithParent,                  ///< run all in the parent's thread

            RunDispatched                   ///< Run state machine via user controlled dispatch( ) API
        };

        /**
         * Ctor
         *
         * @param name State machine name
         *
         */
        StateMachine( String name );
        ~StateMachine( );

        /**
         * Compile state machine in the runtime either in inlined( eString - deafult )
         * form or read from a file. 
         * It is provided that state machine object can have mulpiple behaviours
         * ( state machine bodies ) in the runtime. User can specify which variant
         * to create or use( see switchVariant API )
         *
         * @param variant State machine variant Id
         * @param str Source code string or file name
         * @param type File name or a inlined source code
         *
         * @return Error Error code
         */
        Error create( i::Index variant, const char* str, Type type = eString );

        /**
         * Create state machine from existing prototype/body. See sm_parser.cpp as an
         * example. TBD: create pre-compiler utility to be able to build-in state machine binary format 
         * in the source code.
         *
         * @param variant State machine variant Id
         * @param raw_array State machine in precompiled binary format
         *
         * @return Error Error code
         */
        Error create( i::Index variant, BinCode raw_array );

        /**
         * Start execution of the state machine instance
         *
         * @param mode Execution mode. See class StateMachine for the details
         *
         * @return bool True on success
         */
        Error start( ExecutionMode mode = RunDefault ){ return start( NULL, mode ); }

        /**
         * Start execution of the state machine instance
         *
         * @param parent Parent state machine
         * @param mode Execution mode. See class StateMachine for the details
         *
         * @return bool True on success
         */
        Error start( StateMachine* parent , ExecutionMode mode = RunDefault )
        {
            vector< StateMachine* > context;
            context.insert( context.end( ), this );

            return StateMachine::start( context, parent, mode );
        }

        /**
         * Start execution of the state machine batch
         *
         * @param state_machines vector of the state machine instances to be launched 
         * @param mode Execution mode. RunDefault will force to use execution mode in the sm source code
         *
         * @return bool True on success
         */
        static Error start( vector< StateMachine* > state_machines, ExecutionMode mode = RunDefault )
        {
            return start( state_machines, NULL, mode );
        }

        /**
         * Start execution of the state machine batch
         *
         * @param state_machines vector of the state machine instances to be launched 
         * @param parent Parent state machine
         * @param mode Execution mode. RunDefault will force to use execution mode in the sm source code
         *
         * @return bool True on success
         */
        static Error start( vector< StateMachine* > state_machines, StateMachine* parent, ExecutionMode mode = RunDefault );

        /**
         * Get state machine variant identifier
         *
         * @return Index    Identifier
         */
        i::Index variant( ){ return m_variant; }

        /** 
         * Switch state machine variant on the fly. Possible, 
         * but there are restrictions. Experemental.
         *
         * @param variant  Variant index. Must be registered!
         *
         * @return @li true On success
         */
        bool switch_variant( i::Index variant );

        /**
         * Send a message to the state machine
         *
         * @param event Copy event
         *
         * @return @li eOk                  Event has been queued to the state machine
         *         @li eFailed              Wrong parameter
         *         @li eMachineIsDead       State machine is not active
         *         @li eManagerNotPresent   State machine is not active
         *         @li eOutOfMemory         Out of memory
         */
        Error push_event( Event* event );

        /**
         * Send a message to the state machine
         *
         * @param event_id Event Id
         * @param data Event parameter passed to an action function
         * @param sync_op If sync_op == true, function will return on event processing completion
         *
         * @return @li eOk                  Event has been queued to the state machine
         *         @li eFailed              Wrong parameter
         *         @li eMachineIsDead       State machine is not active
         *         @li eManagerNotPresent   State machine is not active
         *         @li eOutOfMemory         Out of memory
         */
        Error push_event( EventId event_id, void* data = NULL, bool sync_op = false );

        /**
         * Send a message to the state machine
         *
         * @param event_id Event Id
         * @param data Event parameter passed to an action function
         * @param sync_op If sync_op == true, function will return on event processing completion
         *
         * @return @li eOk                  Event has been queued to the state machine
         *         @li eFailed              Wrong parameter
         *         @li eMachineIsDead       State machine is not active
         *         @li eManagerNotPresent   State machine is not active
         *         @li eOutOfMemory         Out of memory
         */
        Error push_event( EventId event_id, int data, bool sync_op = false ){ return StateMachine::push_event( event_id, ( void* )data, sync_op ); }

        /**
         * Send a message to the state machine
         *
         * @param event_id Event Id
         * @param data Event parameter passed to an action function
         * @param sync_op If sync_op == true, function will return on event processing completion
         *
         * @return @li eOk                  Event has been queued to the state machine
         *         @li eFailed              Wrong parameter
         *         @li eMachineIsDead       State machine is not active
         *         @li eManagerNotPresent   State machine is not active
         *         @li eOutOfMemory         Out of memory
         */
        Error push_event( EventId event_id, const char* data, bool sync_op = false ){ return StateMachine::push_event( event_id, ( void* )data, sync_op ); }

        /**
         * Send a message to the state machine
         *
         * @param event_id Event Id
         * @param data Event parameter passed to an action function
         * @param sync_op If sync_op == true, function will return on event processing completion
         *
         * @return @li eOk                  Event has been queued to the state machine
         *         @li eFailed              Wrong parameter
         *         @li eMachineIsDead       State machine is not active
         *         @li eManagerNotPresent   State machine is not active
         *         @li eOutOfMemory         Out of memory
         */
        Error push_event( EventId event_id, Object* data, bool sync_op = false );

        /**
         * Send a message to the state machine. Blocks until finished.
         *
         * @param event_id Event Id
         * @param data Event parameter passed to an action function
         *
         * @return @li eOk                  Event has been queued to the state machine
         *         @li eFailed              Wrong parameter
         *         @li eMachineIsDead       State machine is not active
         *         @li eManagerNotPresent   State machine is not active
         *         @li eOutOfMemory         Out of memory
         */
        Error push_event_sync( EventId event_id, void* data = NULL ){ return push_event( event_id, data, true ); }

        /**
         * Send a message to the state machine. Blocks until finished.
         *
         * @param event_id Event Id
         * @param data Event parameter passed to an action function
         *
         * @return @li eOk                  Event has been queued to the state machine
         *         @li eFailed              Wrong parameter
         *         @li eMachineIsDead       State machine is not active
         *         @li eManagerNotPresent   State machine is not active
         *         @li eOutOfMemory         Out of memory
         */
        Error push_event_sync( EventId event_id, int data ){ return push_event( event_id, ( void* )data, true ); }

        /**
         * Send a message to the state machine. Blocks until finished.
         *
         * @param event_id Event Id
         * @param data Event parameter passed to an action function
         *
         * @return @li eOk                  Event has been queued to the state machine
         *         @li eFailed              Wrong parameter
         *         @li eMachineIsDead       State machine is not active
         *         @li eManagerNotPresent   State machine is not active
         *         @li eOutOfMemory         Out of memory
         */
        Error push_event_sync( EventId event_id, const char* data ){ return push_event( event_id, ( void* )data, true ); }

        /**
         * Send a message to the state machine. Blocks until finished.
         *
         * @param event_id Event Id
         * @param data Event parameter passed to an action function
         *
         * @return @li eOk                  Event has been queued to the state machine
         *         @li eFailed              Wrong parameter
         *         @li eMachineIsDead       State machine is not active
         *         @li eManagerNotPresent   State machine is not active
         *         @li eOutOfMemory         Out of memory
         */
        Error push_event_sync( EventId event_id, Object* data ){ return push_event( event_id, data, true ); }

        /**
         * Get terminate event. User state machine ca reuse system terminate event index 
         * to instruct other state machine to terminate.
         *
         * @return @li Ev_Sys_Terminate     System terminate event index
         */
        int terminate_event_id( );

        /**
         * User controlled dispather API. Only valid with @ref RunDispatched exec mode.
         *
         * @return @li eOk                      If state machine is still active
         *         @li eExecModeError           API should not be used for the state machine exec mode
         *         @li eStateMachineNotActive   State machine finished or never been launched
         */
        Error dispatch( );

        /**
         * Set dispather priority. Set thread priority for the hosting dispather
         *
         * @param priority Thread priority
         *
         * @return @li eOk                      on success
         *         @li eStartStateMachineFirst  if the call is made before the start
         */
        Error set_dispatcher_priority( OsThreadPriority priority );

        /**
         * Change current state heart beat interval and heat beats number. 
         *
         * @param time New state heart beat interval in milliseconds
         * @param counter New state heart beats number
         *
         */
        void reset_state_properties( int time = -1, int counter = -1 );

        /**
         * Get parent. To be able to send messages to the parent
         *
         * @return sea::ref< StateMachine > Parent's state machine
         */
        sea::ref< StateMachine > parent( ) { return m_parent; }

        /**
         * Wait for state machine to finish. This call should be made 
         * from outside of the state machine context - that is in the 
         * thread other than the state machine's.
         *
         * @return Error eOk if state machine finished successfully, eFailed 
         *  on attempt to wait state machine in the same thread
         */
        Error wait( );

        /**
         * Wait for all state machine under the manager the state machine 
         * belongs to to finish
         *
         * @return Error eOk if manager finished successfully, eFailed 
         *  on attempt to wait state machine in the same thread
         */
        Error wait_manager_to_finish( );

        /**
         * Set external OsNotifier to be triggered on state machine completion
         *
         * @param aNotifier External notifier to be triggered on state machine completion
         */
        void set_waiter( OsNotifier* aNotifier );

        /**
         * Set the instance execution mode.
         *
         * @return ExecutionMode If RunDefault is set - binary setting is used
         */
        void set_exec_mode( ExecutionMode mode ){ m_exec_mode = mode; }

        /**
         * Get the instance execution mode
         *
         * @return ExecutionMode If RunDefault is set - binary setting is used
         */
        ExecutionMode exec_mode( ){ return m_exec_mode; }

        /** 
         * Get meta system object for the state machine. Normaly should not be as a public
         * interface. ONLY FOR TEST PURPOSES.
         * 
         * @return MetaSystemRegistryEntry* pointer to the meta system entry
         */
        MetaSystemRegistryEntry* registry( ) { return m_meta.registry( ); }

        /**
         * Get manager. This interface should be used ONLY for 
         * self dispatched state machines - to run more then one machine
         * withing the same manager.
         *
         * @return Manager*      State machine manager
         */
        Manager* dispatcher( ) { return m_manager.get( ); }

        /** 
         * Action function may request mode details on the occuring event
         * 
         * @return Event* Event pointer
         */
        Event* event( ){ return m_current_event.get( ); }

        /** 
         * Get system state machine in order to trigger Ev_Sys_Terminate( ) event
         * 
         * @return StateMachine* pointer to the state machine
         */
        static SystemStateMachine* system( );

        /**
         * Checks if state machine is alive. Use it for lazy initializattion
         *
         * @return bool true/false
         */
        bool is_alive( ) { return m_alive; }

        /** 
         * Print state machine body. Functional when SEA_LOG is defined.
         */
        void print( );

        /** 
         * For debugging ONLY. Control loggin output for the state machine
         */
        void log( bool enable )
        {
            SEA_UNUSED( enable );
#if defined( SEA_LOG )
            m_logging = enable;
#endif
        }

    private:

        /**
         * Get manager. This interface should be used ONLY for 
         * self dispatched state machines - to run more then one machine
         * withing the same manager. \b INTERNAL \b API. 
         *
         * @return sea::ref< Manager > State machine manager
         */
        sea::ref< Manager > manager( ) { return m_manager; }

        /**
         * Start/stop executon of the state machine. If stopped 
         * no events are execpted for processing. \b INTERNAL \b API. 
         *
         * @param  aTrue
         */
        void set_alive( bool aTrue ) { m_alive = aTrue; }

        /** 
         * Get state sequence number. \b INTERNAL \b API. 
         *
         * @return int State's sequence number
         */
        int get_state_stamp( ) { return m_stateStamp; }

        /** 
         * Get state body/prototype. \b INTERNAL \b API. 
         *
         * @return Body* 
         */
        sea::aref body( ) { return m_body; }

        /** 
         * Get state machine field at the offset. \b INTERNAL \b API. 
         *
         * @param offset offset in the compiled code
         *
         * @return int State Machine instruction
         */
        int get_body_field( int offset )
        {
            ASSERT_DEBUG( m_body != NULL );
            if ( m_body != NULL  )
            {
                BinCode body = m_body->as< int >( );
                return body[ offset ];
            }
            return 0;
        }

        /** 
         * Set state machine field at the offset. \b INTERNAL \b API. 
         *
         * @param offset offset in the compiled code
         * @param value New value
         *
         */
        void set_body_field( int offset, int value )
        {
            BinCode body = m_body->as< int >( );
            body[ offset ] = value;
        }

        /** 
         * Get current state. \b INTERNAL \b API. 
         *
         * @return Index Index of the current state
         */
        i::Offset get_state( ) { return m_current_state; }

        /** 
         * Perform transition to the next state. \b INTERNAL \b API. 
         *
         * @param state State offset
         * @param event Event that caused the transition to retieve event parameter to propagade to the
         *              next state
         */
        void goto_next_state( i::Offset state, Event* event );

        /** 
         * Start/restart state timer. \b INTERNAL \b API. 
         */
        void start_state_timer( );

        /** 
         * Interface to stop timer from SystemStateMachine::exit. \b INTERNAL \b API. 
         */
        void stop_state_timer( bool destroy = false ) 
        {
            if( m_timer != NULL  )
            {
                m_timer->stop( ); 
                if ( destroy )
                {
                    m_timer->forget_state_machine( );
                }
            }
        }

        /**
         * Get event prioroty queue for the event type. \b INTERNAL \b API. 
         *
         * @param event_id Event Id
         * @param Manager* State machine manager
         *
         * @return Queue* Event queue where event should go to
         */
        Queue* get_event_queue( EventId event_id, Manager* manager );

        /** 
         * Manager calls this function on Ev_Sys_Destroy to remove the state machine from
         * the list of active state machines. \b INTERNAL \b API. 
         */
        void destroy( );

    protected:

        enum EventMask
        {
            extEventMask = 0,                   ///< External/"normal" events mask. Lowest priority
            intEventMask = 0x40000000,          ///< Internal event mask. Intended for 
                                                ///< events generated by the state machine for internal purposes. Higher priority.
            sysEventMask = 0x80000000           ///< System event mask. Reserved for system/state machine framework events. Highest priority
        };

    private:

        MetaSystem m_meta;                      ///< Meta System for the state machine
        sea::ref< Timer > m_timer;              ///< Re-usable state timer
        int m_stateStamp;                       ///< Every state is stamped by the sequence number 
                                                ///< for system event filterring
        bool m_alive;                           ///< True until exit( ) action function is called

        i::Offset m_current_state;              ///< Current state offset
        sea::ref< Event > m_current_event;      ///< While in action function it can request more info about the event
        int m_counter;                          ///< Counting state counter
                                       
        sea::aref m_body;                       ///< Compiled state machine pointer
        sea::ref< Manager > m_manager;          ///< Cached pointer to the manager
        sea::ref< StateMachine > m_parent;      ///< Cached pointer to the parent
        
        Parser* m_parser;                       ///< Compiler
        i::Index m_variant;                     ///< State machine variant id used to choose state machine body to use
        OsNotifier* m_exit_notifier;            ///< External signal to be triggered on state machine completion
        ExecutionMode m_exec_mode;              ///< If RunDefault - get it from binary buffer
        static SystemStateMachine* m_system;    ///< Pointer to the system state machine
        bool m_in_dispatcher;                   ///< Prevent reentry for user_dispatched state machines

#if defined( SEA_LOG )
        String m_current_state_name;
        String m_current_event_name;
        bool m_logging;                         ///< To control state machine logging while SEA_LOG is enabled
#endif
    };


    /**
     * @def STATEMACHINE_NAME
     * @brief API to get state machine name
     */
    #define STATEMACHINE_NAME ( __FUNCTION__ )
    /*#define STATEMACHINE_NAME ( typeid( this ).name( ) )*/

    /**
     * @class MetaAction
     * @brief Meta action object ensures action function enlistment in the state machine registry. \b INTERNAL \b API. 
     *
     * @ingroup StateMachineFramework
     */
    class MetaAction
    {
        friend class Manager;
        friend class StateMachine;
        friend class Parser;

    public:
        MetaAction( ) :
            m_name( NULL ),
            m_function( NULL ),
            m_index( -1 ) { }
        MetaAction(
            String class_name,
            const char* action_name,
            StateMachine::ActionFunction action_function,
            bool aSystem = false ):
                m_name( action_name ),
                m_function( reinterpret_cast< StateMachine::ActionFunction >( action_function ) ),
                m_index( -1 )
        {
            sea::ref< MetaSystemRegistryEntry > registry_entry;
            {
                OsMutex::Scoped safe( &GlobalMetaSystemRegistry::m_lock );
                registry_entry = GlobalMetaSystemRegistry::get( )[ class_name ].get( );
            }
            MetaSystemRegistryEntry* entry = registry_entry.get( );

            OsMutex::Scoped safe( &entry->m_lock );

            // Failure of the assert means that user class is not inherited from the StateMachine
            // Or name passed into ctor is incorrect
            ASSERT_DEBUG( entry );

            map< string, MetaAction >& actions = entry->m_action_map;
            map< string, MetaAction >::iterator it = actions.find( action_name );

            if ( it == actions.end( ) )
            {
                vector< MetaAction >& aarray = entry->m_actions;
                m_index = aarray.size( ) + ( aSystem ? StateMachine::sysEventMask : StateMachine::extEventMask );
                aarray.insert( aarray.end( ), *this );
                entry->m_action_map[ action_name ] = *this;
            }
            else
            {
                m_index = it->second.index( );
            }
        }

        /** 
         * Get action name. For DEBUG purpose ONLY!
         *
         * @return Action name
         */
        const char* name( ) { return m_name; }

        /** 
         * Get action index. For DEBUG purpose ONLY!
         *
         * @return Action index
         */
        int index( ) { return m_index; }

    private:

        StateMachine::ActionFunction get_function( ) { return m_function; }

    private:
        const char* m_name;                         // Action function name
        StateMachine::ActionFunction m_function;    // Action function pointer
        int m_index;                                // Index in the array. Cross reference is needed 
                                                    // when state machine is parsed from JSON/XML presentation
    };

    /**
     * @def STATIC_META_DATA_ENABLED
     * @brief 
     * See STATIC_META_ACTION_ENABLED description
     */
    #if defined( STATIC_META_DATA_ENABLED )
        #define SEA_STATIC_META_DATA_DEF static
        #define STATIC_META_ACTION_IMPL( CLASS, X ) CLASS::act_##X CLASS::the_##X;

        #define EVENT_IMPL( CLASS, X ) CLASS::ext_event_##X CLASS::X
        #define EVENT_INT_IMPL( CLASS, X ) CLASS::int_event_##X CLASS::X
        #define EVENT_SYS_IMPL( CLASS, X ) CLASS::sys_event_##X CLASS::X

    #else
        #define SEA_STATIC_META_DATA_DEF
        #define STATIC_META_ACTION_IMPL( CLASS, X )

        #define EVENT_IMPL( CLASS, X )
        #define EVENT_INT_IMPL( CLASS, X )
        #define EVENT_SYS_IMPL( CLASS, X )

    #endif

    /**
     * @def ACT
     * @brief 
     * API to declare action function.
     * Example:
     *  declaration:    ACT( sm_class, my_action_function );
     *  runtime use:    void my_action_function( void* param ){ }
     */
    #define ACT( CLASS, X ) \
        class act_##X : public sea::MetaAction \
        { \
        public: \
            act_##X( ) : MetaAction( \
                #CLASS, \
                #X, \
                static_cast< StateMachine::ActionFunction >( &CLASS::X ) ){ } \
        }; \
        SEA_STATIC_META_DATA_DEF act_##X the_##X; \
        virtual void X( void* param )

    /**
     * @def ACT_SYS
     * @brief 
     * API to declare system action function.
     * Example:
     *  declaration:    ACT_SYS( sm_class, my_action_function );
     *  runtime use:    void my_action_function( void* param ){ }
     */
    #define ACT_SYS( CLASS, X ) \
        class act_##X : public sea::MetaAction \
        { \
        public: \
            act_##X( ) : MetaAction( \
                #CLASS, \
                #X, \
                static_cast< StateMachine::ActionFunction >( &CLASS::X ), \
                true ){ } \
        }; \
        SEA_STATIC_META_DATA_DEF act_##X the_##X; \
        virtual void X( void* param )

    /**
     * @def  ACT_IMPL
     * @brief 
     * Action function implementation macro
     */
    #define ACT_IMPL( CLASS, X ) \
        STATIC_META_ACTION_IMPL( CLASS, X ) \
        void CLASS::X( void* event_param )

    /**
     * @def 
     * @brief Access the event parameter passed to the action function
     */
    #define ACT_EVENT_PARAM( ) event_param

    /**
     * @class MetaEvent
     * @brief Meta event object ensures an event enlistment in the state machine registry
     *
     * @ingroup StateMachineFramework
     */
    class MetaEvent
    {
        friend class Manager;
        friend class StateMachine;
        friend class SystemStateMachine;
        friend class Parser;

    public:
        MetaEvent( ) :
            m_name( NULL ),
            m_index( -1 ) { }

        MetaEvent( 
            const char* event_name, 
            String class_name, 
            StateMachine::EventMask mask = StateMachine::extEventMask ) : 
                m_name( event_name ),
                m_index( -1 )
        {
            sea::ref< MetaSystemRegistryEntry > registry_entry;
            {
                OsMutex::Scoped safe( &GlobalMetaSystemRegistry::m_lock );
                registry_entry = GlobalMetaSystemRegistry::get( )[ class_name ];
            }
            MetaSystemRegistryEntry* entry = registry_entry.get( );

            OsMutex::Scoped safe( &entry->m_lock );

            // Failure of the assert means that user class is not inherited from the StateMachine
            // Or name passed into ctor is incorrect
            ASSERT_DEBUG( entry );

            map< string, MetaEvent >& events = entry->m_event_map;
            map< string, MetaEvent >::iterator it = events.find( event_name );

            if ( it == events.end( ) )
            {
                vector< MetaEvent >& aarray = entry->m_events;
                m_index = aarray.size( ) | mask;
                aarray.insert( aarray.end( ), *this );
                entry->m_event_map[ event_name ] = *this;
            }
            else
            {
                m_index = it->second.index( );
            }
        }

        /** 
         * Get event index
         *
         * @return Event Index
         */
        int operator( )( ) { return m_index; }

        /** 
         * Get event index. More of a declarative way
         *
         * @return Event index
         */
        int index( ) { return m_index; }

        /** 
         * Get event name. For DEBUG purpose ONLY!
         *
         * @return Event name
         */
        const char* name( ) { return m_name; }

    private:
        const char* m_name;
        int m_index;                                // Index in the array. Cross reference is needed 
                                                    // When state machine is parsed from JSON/XML presentation
    };

    /**
     * @def EVENT_EXT
     * @brief 
     * API to declare public event.
     * Example:
     *  declaration:    EVENT_EXT( my_event );
     *  runtime use:    my_state_machine.my_event( );
     */
    #define EVENT_EXT( CLASS, X ) \
        class ext_event_##X : public sea::MetaEvent \
        { \
        public: \
            ext_event_##X( ) : MetaEvent( \
                #X, \
                #CLASS ) { } \
        }; \
        SEA_STATIC_META_DATA_DEF ext_event_##X X

    /**
     * @def EVENT_INT
     * @brief 
     * API to declare private event
     * Example:
     *  declaration:    EVENT_INT( my_event );
     *  runtime use:    my_state_machine.my_event( );
     */
    #define EVENT_INT( CLASS, X ) \
        class int_event_##X : public sea::MetaEvent \
        { \
        public: \
            int_event_##X( ) : MetaEvent( \
                #X, \
                #CLASS, \
                intEventMask ) { } \
        }; \
        SEA_STATIC_META_DATA_DEF int_event_##X X

    /**
     * @def EVENT_SYS
     * @brief 
     * API to declare system event. Reserved for the
     * State machine framework
     * Example:
     *  declaration:    EVENT_SYS( my_event );
     *  runtime use:    my_state_machine.my_event( );
     */
    #define EVENT_SYS( CLASS, X ) \
        class sys_event_##X : public sea::MetaEvent \
        { \
        public: \
            sys_event_##X( ) : MetaEvent( \
                #X, \
                #CLASS, \
                sysEventMask ) { } \
        }; \
        SEA_STATIC_META_DATA_DEF sys_event_##X X


    /**
     * @class SystemStateMachine
     * @brief 
     *  This class contains system/predefined events and action functions. \b INTERNAL \b API. 
     *
     * @ingroup StateMachineFramework
     */
    class SystemStateMachine : public StateMachine
    {
        friend class Engine;
        friend class Manager;
        friend class StateMachine;

    private:
        SystemStateMachine( ) : StateMachine( STATEMACHINE_NAME ) { }

        ACT_SYS( SystemStateMachine, exit );                                  ///< System action function. Must be invoked at the exit
                                                                              ///< of the state machine
        /* -1 */ EVENT_SYS( SystemStateMachine, Ev_Sys_Create );              ///< start/restart state machine
        /* -2 */ EVENT_SYS( SystemStateMachine, Ev_Sys_Destroy );             ///< stop state machine
        /* -3 */ EVENT_SYS( SystemStateMachine, Ev_Sys_OnEntry );             ///< on state entry event
        /* -4 */ EVENT_SYS( SystemStateMachine, Ev_Sys_Heartbeat );           ///< heart beat timeout elapsed
        /* -5 */ EVENT_SYS( SystemStateMachine, Ev_Sys_LastHeartbeat );       ///< last heart beat timeout elapsed
        /* -6 */ EVENT_SYS( SystemStateMachine, Ev_Sys_Terminate );           ///< command/event to terminate state machine
        /* -7 */ EVENT_SYS( SystemStateMachine, Ev_Sys_ChildTerminated );     ///< child state machine has been stopped
        /* -8 */ EVENT_SYS( SystemStateMachine, Ev_Sys_ParentTerminated );    ///< parent state machine has been stopped
        /* -9 */ EVENT_SYS( SystemStateMachine, Ev_Sys_NoEvent );             ///< dummy event
    };

    /**
     * @def SM_DECLARE
     * @brief 
     * Helpers to define string presentation of a state machine
     */
    #define SM_DECLARE( x, y ) const char* x = #y
}
#endif
